/**
 * \file grille.c
 * \version 0.35
 *
 * Contient le corps des fonctions permettant l'affichage et le déplacement de la grille de sudoku
 */

#include "grille.h"
#include "check.h"

struct grille
{
  char* nombre;
  char* fixed;
  unsigned char* filled;
  unsigned char posX;
  unsigned char posY;
  mode m;
};

char* tableInit(unsigned char n, char v)
{
  char* t = malloc(n * sizeof(char));
  unsigned char i;
  for(i = 0; i <= n; i++)
    t[i] = v;
  return t;
}

void tableFill(char* t, unsigned char n, char v)
{
  unsigned char i;
  for(i = 0; i < n; i++)
  {
    t[i] = v;
  }
}

grille Gnew()
{
  grille g = malloc(sizeof(struct grille));
  if( g == NULL )
    return NULL;
  g->nombre = tableInit(81, 0);
  g->fixed = tableInit(81, 0);
  g->filled = tableInit(10, 0);
  g->filled[0] = 81;
  g->posX = 1;
  g->posY = 1;
  g->m = EASY;
  return g;
}

void Grestart(grille g)
{
  tableFill(g->nombre, 81, 0);
  tableFill(g->fixed, 81, 0);
  tableFill(g->filled, 10, 0);
  g->filled[0] = 81;
  g->posX = 1;
  g->posY = 1;
  g->m = EASY;
}

void Greset(grille g)
{
  unsigned char i;
  for(i = 0; i < 81; i++)
  {
    if( g->fixed[i] == 0 )
      GsetNumberIndex(g, i, 0); 
  }
}

void Gload(grille g, unsigned char* t)
{
  unsigned char i;
  for(i = 0; i < 81; i++)
  {
    GsetNumberIndex(g, i, t[i]);
  }
  GfixGrid(g);
}

void Gfree(grille g)
{
  if( g != NULL )
  {
    if( g->nombre != NULL ) free(g->nombre);
    if( g->fixed != NULL ) free(g->fixed);
    if( g->filled != NULL ) free(g->filled);
    free(g);
  }
}

void Gmove(grille g, direction d)
{
  char dx = 0, dy = 0;
  switch(d)
  {
    case UP:
      dy = -1;
      break;
    case DOWN:
      dy = 1;
      break;
    case LEFT:
      dx = -1;
      break;
    case RIGHT:
      dx = 1;
      break;
  }
  g->posX = BORNER(g->posX + dx, 1, 9);
  g->posY = BORNER(g->posY + dy, 1, 9);
}

void GfixGrid(grille g)
{
  unsigned char i ;
  for(i = 0; i < 81; i++)
  {
    if( GgetNumberIndex(g, i) > 0 )
      g->fixed[i] = 1;
    else
      g->fixed[i] = 0;
  }
}

void Gfilled(grille g, unsigned char decr, unsigned char incr)
{
  g->filled[decr]--;
  g->filled[incr]++;
}

void GresetNotFixedBoxes(grille g)
{
  unsigned char i;
  for(i = 0; i < 81; i++)
  {
    if( g->fixed[i] == 0 )
    {
      Gfilled(g, g->nombre[i], 0);
      g->nombre[i] = 0;
    }
  }
}
unsigned char GgetPosX(grille g)
{
  return g->posX;
}

unsigned char GgetPosY(grille g)
{
  return g->posY;
}

unsigned char Gindex(unsigned char x, unsigned char y)
{
  return (y-1)*9 + x - 1;
}

unsigned char GgetNumber(grille g, unsigned char x, unsigned char y)
{
  return g->nombre[Gindex(x, y)];
}

unsigned char GgetCurrentNumber(grille g)
{
  return GgetNumber(g, GgetPosX(g), GgetPosY(g));
}

unsigned char GgetNumberIndex(grille g, unsigned char i)
{
  return g->nombre[i];
}

unsigned char GgetFixedIndex(grille g, unsigned char i)
{
  return g->fixed[i];
}

unsigned char GgetFilled(grille g, unsigned char i)
{
  return g->filled[i];
}

void GsetNumber(grille g, unsigned char x, unsigned char y, unsigned char n)
{
  Gfilled(g, g->nombre[Gindex(x,y)], n);
  g->nombre[Gindex(x,y)]=n;
}

void GsetCurrentNumber(grille g, unsigned char n)
{
  GsetNumber(g, GgetPosX(g), GgetPosY(g), n);
}

void GsetNumberIndex(grille g, unsigned char i, unsigned char n)
{
  Gfilled(g, g->nombre[i], n);
  g->nombre[i]=n;
}

char GtryToSetCurrentNumber(grille g, unsigned char n)
{
  if( GgetFixedIndex(g, Gindex(GgetPosX(g), GgetPosY(g))) == 0 )
  {
    unsigned char tmp = GgetCurrentNumber(g);
    GsetCurrentNumber(g, n);
    if( CCurrentBox(g) )
      return 1;
    else
    {
      GsetCurrentNumber(g, tmp);
      return -1;
    }
  }
  else
    return -1;
}

void Gdisplay(grille g) {
  // cadre principal
  DrawLine(CORNER_X,CORNER_Y,CORNER_X,CORNER_Y+90,A_THICK1);
  DrawLine(CORNER_X+90,CORNER_Y,CORNER_X+90,CORNER_Y+90,A_THICK1);
  DrawLine(CORNER_X,CORNER_Y,CORNER_X+90,CORNER_Y,A_THICK1);
  DrawLine(CORNER_X,CORNER_Y+90,CORNER_X+90,CORNER_Y+90,A_THICK1);
  // grilles 3x3
  DrawLine(CORNER_X+30,CORNER_Y,CORNER_X+30,CORNER_Y+90,A_THICK1);
  DrawLine(CORNER_X+60,CORNER_Y,CORNER_X+60,CORNER_Y+90,A_THICK1);
  DrawLine(CORNER_X,CORNER_Y+30,CORNER_X+90,CORNER_Y+30,A_THICK1);
  DrawLine(CORNER_X,CORNER_Y+60,CORNER_X+90,CORNER_Y+60,A_THICK1);
  // inner lines verticales
  DrawLine(CORNER_X+10,CORNER_Y,CORNER_X+10,CORNER_Y+90,A_NORMAL);
  DrawLine(CORNER_X+20,CORNER_Y,CORNER_X+20,CORNER_Y+90,A_NORMAL);
  DrawLine(CORNER_X+40,CORNER_Y,CORNER_X+40,CORNER_Y+90,A_NORMAL);
  DrawLine(CORNER_X+50,CORNER_Y,CORNER_X+50,CORNER_Y+90,A_NORMAL);
  DrawLine(CORNER_X+70,CORNER_Y,CORNER_X+70,CORNER_Y+90,A_NORMAL);
  DrawLine(CORNER_X+80,CORNER_Y,CORNER_X+80,CORNER_Y+90,A_NORMAL);
  // inner lines horizontales
  DrawLine(CORNER_X,CORNER_Y+10,CORNER_X+90,CORNER_Y+10,A_NORMAL);
  DrawLine(CORNER_X,CORNER_Y+20,CORNER_X+90,CORNER_Y+20,A_NORMAL);
  DrawLine(CORNER_X,CORNER_Y+40,CORNER_X+90,CORNER_Y+40,A_NORMAL);
  DrawLine(CORNER_X,CORNER_Y+50,CORNER_X+90,CORNER_Y+50,A_NORMAL);
  DrawLine(CORNER_X,CORNER_Y+70,CORNER_X+90,CORNER_Y+70,A_NORMAL);
  DrawLine(CORNER_X,CORNER_Y+80,CORNER_X+90,CORNER_Y+80,A_NORMAL);
  // Affichage de la case sélectionnée
  GgridCurrentBox(g);
}

unsigned char GgetXBox(unsigned char x)
{
  unsigned char xx = BORNER(x, 1, 9);
  if( xx < 4 )
    return CORNER_X + 2 + 10 * (xx-1);
  else if( xx < 7 )
    return CORNER_X + 32 + 10 * (xx-4);
  else
    return CORNER_X + 62 + 10 * (xx-7);
}

unsigned char GgetYBox(unsigned char y)
{
  unsigned char yy = BORNER(y, 1, 9);
  if( yy < 4 )
    return CORNER_Y + 2 + 10 * (yy-1);
  else if( yy < 7 )
    return CORNER_Y + 32 + 10 * (yy-4);
  else
    return CORNER_Y + 62 + 10 * (yy-7);
}

void GdisplayNumber(grille g, unsigned char x, unsigned char y)
{
  unsigned char dx = GgetXBox(x);
  unsigned char dy = GgetYBox(y);
  //FontSetSys(F_4x6); // écriture petite taille
  if( GgetNumber(g, x, y) > 0 )
  {
    if( GgetFixedIndex(g, Gindex(x, y)) )
      DrawChar(dx, dy, GgetNumber(g, x, y) + '0', A_REVERSE);
    else
      DrawChar(dx, dy, GgetNumber(g, x, y) + '0', A_NORMAL);
  }
  FontSetSys(F_6x8); // écriture taille normale pour la suite du programme
}

void GdisplayAllNumbers(grille g)
{
  unsigned char i, j;
  for( i = 1; i < 10; i++ )
  {
    for( j = 1; j < 10; j++ )
      GdisplayNumber(g, i, j);
  }
}

void GgridCurrentBox(grille g)
{
  unsigned char x = GgetPosX(g);
  unsigned char y = GgetPosY(g);
  unsigned char xx = CORNER_X+(x-1)*10;
  unsigned char yy = CORNER_Y+(y-1)*10;

  DrawLine(xx, yy, xx, yy+10, A_THICK1);
  DrawLine(xx, yy, xx+10, yy, A_THICK1);
  DrawLine(xx+10, yy, xx+10, yy+10, A_THICK1);
  DrawLine(xx, yy+10, xx+10, yy+10, A_THICK1);
}

unsigned char GisOver(grille g)
{
  if( g->filled[0] == 0 )
        return 1;
  else
    return 0;
}

void GchangeMode(grille g, mode m)
{
  g->m = m;
}

void GnextMode(grille g)
{
  switch(g->m)
  {
    case EASY:
      g->m = MEDIUM;
      break;
    case MEDIUM:
      g->m = HARD;
      break;
    case HARD:
      g->m = EVIL;
      break;
    case EVIL:
      g->m = EASY;
      break;
  }
}

char* GgetMode(grille g)
{
  switch(g->m)
  {
    case EASY:
      return "Easy";
      break;
    case MEDIUM:
      return "Medium";
      break;
    case HARD:
      return "Hard";
      break;
    case EVIL:
      return "Evil";
      break;
    default:
      return "Easy";
  }
}

mode GgetMode2(grille g)
{
	return g->m;
}